/**
* Copyright 2012 nabla
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package com.nabla.wapp.client.ui.form;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.user.client.ui.Widget;
import com.nabla.wapp.client.general.Assert;
import com.nabla.wapp.client.general.LoggerFactory;
import com.nabla.wapp.client.model.AsyncGetDefaultValuesCallback;
import com.nabla.wapp.client.model.Model;
import com.nabla.wapp.client.ui.IHasWidgets;
import com.nabla.wapp.shared.dispatch.IAction;
import com.nabla.wapp.shared.dispatch.StringResult;
import com.nabla.wapp.shared.general.IHasValue;
import com.nabla.wapp.shared.signal.Signal;
import com.nabla.wapp.shared.slot.ISlotManager;
import com.smartgwt.client.data.DSCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.widgets.form.ValuesManager;


public class Form extends com.smartgwt.client.widgets.form.DynamicForm implements IHasWidgets {

	private static final Logger		log = LoggerFactory.getLog(Form.class);
	private Model						model;
	private final Signal				sigSuccess = new Signal();
	private final Signal				sigFailure = new Signal();
	private boolean					saving = false;

	public Form() {
		setSelectOnFocus(true);
		setAutoFocus(true);
	}

	// TODO: remove when SmartGWT bug corrected!
	@SuppressWarnings("unchecked")
	@Override
	public Record getValuesAsRecord() {
		final Map values = getValues();
		final Map m2 = new HashMap();
		m2.putAll(values);
		m2.remove("__ref");
		return new Record(JSOHelper.convertMapToJavascriptObject(m2));
	}

	public Model getModel() {
		return model;
	}

	public void setModel(final Model model) {
		this.model = model;
		this.setDataSource(model);
	}

	@Override
	public void setValuesManager(final ValuesManager values) {
		super.setValuesManager(values);
		final DataSource ds = values.getDataSource();
		if (ds instanceof Model)
			model = (Model)ds;
		this.setDataSource(ds);
	}

	public void setColumnWidths(final String widths) {
		Assert.argumentNotNull(widths);

		final String[] aWidth = widths.split(",");
		this.setNumCols(aWidth.length);
		final Object[] args = new Object[aWidth.length];
		for (int i = 0; i < aWidth.length; ++i) {
			try {
				args[i] = Integer.valueOf(aWidth[i]);
			} catch (final NumberFormatException _) {
				args[i] = aWidth[i].trim();
			}
		}
		this.setColWidths(args);
	}

	public ISlotManager getSuccessSlots() {
		return sigSuccess;
	}

	public ISlotManager getFailureSlots() {
		return sigFailure;
	}

	@Override
	public void add(Widget w) {
		w = Helper.onCreate(w);
		if (w instanceof FormItemIconList)
			setFields(Helper.extendArray(getFields(), ((FormItemIconList)w).getBuddy()));
		else {
			Assert.state(w instanceof IFormItem<?>);
			setFields(Helper.extendArray(getFields(), ((IFormItem<?>)w).getImpl()));
		}
	}

	@Override
	public void clear() {}

	@Override
	public Iterator<Widget> iterator() {
        return null;
	}

	@Override
	public boolean remove(@SuppressWarnings("unused") final Widget w) {
        return false;
	}

	public IHasValue<String> getStringField(final String name) {
		return new IHasValue<String>() {
			@Override
			public String getValue() {
				return Form.this.getValueAsString(name);
			}

			@Override
			public void setValue(final String value) {
				Form.this.setValue(name, value);
			}
		};
	}

	public IHasValue<Integer> getIntegerField(final String name) {
		return new IHasValue<Integer>() {
			@Override
			public Integer getValue() {
				return (Integer) Form.this.getValue(name);
			}

			@Override
			public void setValue(final Integer value) {
				Form.this.setValue(name, value);
			}
		};
	}

	public boolean isSaving() {
		return saving;
	}

	public void setSaving(final boolean saving) {
		this.saving = saving;
	}

	@Override
	public void saveData() {
		if (!isSaving()) {
			if (validate()) {
				setSaving(true);
				super.saveData(new DSCallback() {
					@Override
					public void execute(final DSResponse response, @SuppressWarnings("unused") final Object rawData, @SuppressWarnings("unused") final DSRequest request) {
						setSaving(false);
						if (response.getStatus() == DSResponse.STATUS_SUCCESS)
							sigSuccess.fire();
						else
							sigFailure.fire();
					}
				});
			} else {
				sigFailure.fire();
			}
		} else {
			log.warning("prevented form to save data while already in the process of saving data");
		}
	}

	@Override
	public void saveData(@SuppressWarnings("unused") final DSCallback callback) {
		Assert.state(false);
	}

	public <A extends IAction<StringResult>> void editNewRecordWithDefault(final A getDefaultValuesCommand) {
		Assert.notNull(model.getDispatcher());

		model.getDispatcher().execute(getDefaultValuesCommand, new AsyncGetDefaultValuesCallback() {
			@Override
			public void onFailure(final Throwable caught) {
				log.log(Level.WARNING, "fail to load parameter default values", caught);
				editNewRecord();
			}

			@Override
			public void onDefaultValues(final Map values) {
				if (values != null)
					editNewRecord(values);
				else
					editNewRecord();
			}
		});
	}

}
